Spring SecurityでWebの認証と認可を制御する
よく訓練されたアップル信者、都元です。Webアプリケーションを書くにあたって、誰でも自由に全てのリクエストが呼べるなんていうユルユルな要件ってのはほとんど無いと思います。誰がからのアクセスかが特定できて、そのユーザが適切な権限を持っている場合に限り、アクセスが成功する必要があるでしょう。
Spring Securityの導入 v14.0
Spring Frameworkの世界の中で認証と認可を司るコンポーネントが Spring security です。
Spring BootプロジェクトでSpring securityを使うには、まずは依存ライブラリとして spring-boot-starter-security
を追加します。え、まさかこれだけ? → GitHub diff
さぁさぁ、起動してみましょう。(以降、タイトルに示したバージョンのブランチをcheckoutして実行できるようにしてあります。)
$ git clone https://github.com/classmethod-aws/berserker.git $ cd berserker $ git checkout 14.0 $ ./gradlew bootRun (略) 2016/05/26 14:14:25.960 [host-startStop-1] INFO o.s.b.a.s.AuthenticationManagerConfiguration:170 - Using default security password: 8dad0c63-b777-4f11-9231-30d7f221eba6 (略) 2016/05/26 14:14:28.495 [main] INFO o.s.b.c.e.t.TomcatEmbeddedServletContainer:162 - Tomcat started on port(s): 8080 (http) 2016/05/26 14:14:28.503 [main] INFO j.c.e.berserker.BerserkerApplication:57 - Started BerserkerApplication in 6.301 seconds (JVM running for 7.233)
細かい設定をしないと、user
というユーザに対して、自動生成したパスワードを設定して起動してくれます。自動生成パスワードは、起動ログ中に見えていますね。
APIエンドポイントへのアクセス
早速アクセスしてみます。
$ curl -s http://localhost:8080/users | jq . { "timestamp": 1464239892131, "status": 401, "error": "Unauthorized", "message": "Full authentication is required to access this resource", "path": "/users" }
このように、クレデンシャルを特に指定しないと、401 Unauthorized
となります。
$ curl -s \ -H "Authorization: Basic $(echo -n "user:8dad0c63-b777-4f11-9231-30d7f221eba6" | base64)" \ http://localhost:8080/users | jq . [ { "username": "miyamoto" }, { "username": "yokota" } ]
そして、Basic認証の仕様に従って Authorization ヘッダを付けてやると、今まで通りの結果が得られました。
Web画面へのアクセス
同様に、/
にブラウザからアクセスしてみると、Basic認証ダイアログが表示されますね。
ここにユーザ名とパスワードを入力してやると、従来通りののページが表示できるはずです。
ユーザを静的に設定する v14.1
ここまでのような無設定ですと、パスワードは起動毎に変わってしまう状態なので、実用的ではありません。ユーザ名とパスワードを固定化する設定を書いてみましょう。 → GitHub diff
Webに関わるセキュリティ設定は、WebSecurityConfigurerAdapter
を継承したクラスに@Configuration
と@EnableWebSecurity
を付与して行います。
設定では configure(AuthenticationManagerBuilder)
メソッドをオーバーライドし、下記の2ユーザを定義しました。
- ユーザ名:
miyamoto
, パスワード:pass
, 権限:ROLE_USER
- ユーザ名:
yokota
, パスワード:word
, 権限:ROLE_ADMIN
v14.1をチェックアウト&起動して試してみてください。
$ curl -s \ -H "Authorization: Basic $(echo -n "miyamoto:pass" | base64)" \ http://localhost:8080/users | jq . [ { "username": "miyamoto" }, { "username": "yokota" } ]
ユーザをDBから読み込む v14.2
そう、MySQLにusersテーブルを用意してせっかくユーザを登録しているわけですから。。。その情報で認証したいですよね。 → GitHub diff
というわけで設定では configure(AuthenticationManagerBuilder)
メソッドにおいて inMemoryAuthentication
ではなく jdbcAuthentication
を構成してみました。SQL等が設定されていて少々武骨ですがご容赦を。
また、パスワードはハッシュ化して記録してありますので、そのハッシュ関数に対応するBCryptPasswordEncoder
を設定しておきます。
で、ここで一点お詫びなんですが、今まで使ってきたハッシュ化パスワードの平文を忘れてしまいましたw なので、flywayを使ってテーブルスキーマ更新と共にパスワードを更新しています。ご了承くださいw
v14.2をチェックアウト&起動して試してみてください。v14.1と動きは変わりませんけど。。。とりあえずDBに基いて認証をしています。
フォーム認証を行う v14.3
APIであればBasic認証もアリだと思うのですが、UIの場合はHTMLによるフォーム認証が一般的です。そのように設定してみました。 → GitHub diff
configure(HttpSecurity)
メソッドをオーバーライドし、下記の設定を行いました。
1つ目は、全てのリクエストにおいて、認証が必要であること。
http.authorizeRequests() .anyRequest().authenticated();
2つ目は、フォームによる認証を行うこと。
http.formLogin();
さらに、index.html
を修正して、ログアウトボタンも付けてみました。
v14.3をチェックアウト&起動し、ブラウザで http://localhost:8080/
にアクセスしてみてください。下記の通り、/login
に自動的にリダイレクトされ、ログイン画面を表示します。
認証が成功すると、/
を表示します。
ログアウトボタンを押すと、再びログイン画面に戻ります。
パス毎にセキュリティ要件を設定する v14.4
さて、ここまでの設定では全てのエンドポイントに対するアクセスが要認証となってしまいます。そこでエンドポイントを幾つか追加し、パス毎に必要な条件を設定してみました。 → GitHub diff
パス | 条件 |
---|---|
/public |
認証なしで誰でもアクセスできる。 |
/admin |
認証済みで、ROLE_ADMIN 権限を持っている場合のみアクセスできる。 |
/ 、というか上記以外全部 |
認証していれば権限に関わらずアクセスできる。未認証はダメ。 |
というのが、これです。
http.authorizeRequests() .antMatchers("/public").permitAll() .antMatchers("/admin").hasAuthority("ROLE_ADMIN") .anyRequest().authenticated();
v14.4をチェックアウト&起動して試してみてください。miyamoto
(ROLE_ADMINを持っていない)でログインして/admin
にアクセスした場合の画面だけ、示しておきます。
フォームログインしたりBasic認証したり…。
さて、ここで本連載に関する決断を一つさせていただこうと思います。この先、この連載でHTMLによるWeb-UIアプリケーションを書いていくのか、JSONによるWeb-APIアプリケーションを書いていくのか。
それぞれをご紹介できれば、Springの魅力をより広く皆様にお届けできると思うのですが、併記していくのも読みづらいものです。例えば、未認証でUIにアクセスした場合は「ログインページにリダイレクト」、未認証でAPIにアクセスした場合は「401 Unauthorized」を返すべき等の複雑な要件に対しては、設定も複雑になってしまいます。
ここは断腸の思いで一旦「JSONによるWeb-APIアプリケーション」の方に倒そうと思います。その方針に従ってフォーム認証は廃止し、ひとまずBasic認証のみとして整理したものが、v14.5 になります。
よろしくお願いします。